package com.bestcem.xm.ticket.task;

import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.ticket.dao.NotificationDao;
import com.bestcem.xm.ticket.entity.mongo.Notification;
import com.bestcem.xm.ticket.enums.NotificationTypeEnum;
import com.bestcem.xm.ticket.enums.ProjectStatusEnum;
import com.bestcem.xm.ticket.enums.SMSRecordTypeEnum;
import com.bestcem.xm.ticket.grpc.client.TicketBaseQdesGrpcService;
import com.bestcem.xm.ticket.grpc.client.TicketBaseUserGrpcService;
import com.bestcem.xm.ticket.grpc.client.TicketSurveyGrpcService;
import com.bestcem.xm.ticket.grpc.client.dto.qdes.QdesDTO;
import com.bestcem.xm.ticket.grpc.client.dto.user.OrganizationDTO;
import com.bestcem.xm.ticket.mq.send.biz.impl.TicketMessageSendServiceImpl;
import com.bestcem.xm.ticket.service.dto.mq.TicketSendDTO;
import com.bestcem.xm.ticket.service.dto.user.EmailContentDTO;
import com.bestcem.xm.ticket.service.manager.notify.NotifyManager;
import com.bestcem.xm.ticket.util.NotificationUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

import static com.bestcem.xm.common.core.uitls.DateUtil.BEIJING_ZONE;

/**
 * 通知异步任务
 *
 * @author gy.liu
 * @version 1.0
 * @date 2021/8/30 13:53
 **/
@Slf4j
@Component
public class NotificationTask {
    @Resource
    TicketBaseUserGrpcService ticketBaseUserGrpcService;

    @Resource
    NotificationDao notificationDao;

    @Resource
    TicketBaseQdesGrpcService ticketBaseQdesGrpcService;

    @Resource
    NotificationUtil notificationUtil;

    @Resource
    TicketSurveyGrpcService ticketSurveyGrpcService;


    //    @Resource
//    private TicketDelayMqSender ticketDelayMqSender;
    @Resource
    private TicketMessageSendServiceImpl ticketMessageService;

    @Resource
    private NotifyManager notifyUtil;

    @Value("${app.ticket.task.notify-time:9-0-0}")
    String notifyTime;

    public static String NOTIFICATION_TYPE_DAY = "day";
    public static String NOTIFICATION_TYPE_WEEK = "week";
    public static String NOTIFICATION_TYPE_MONTH = "month";

    /**
     * @param surveyId  问卷id
     * @param receivers 接收人
     * @param content   内容
     * @param orgId     组织id
     * @author gy.liu
     * @date 2021/8/30
     */
    public void sendNotificationSms(String surveyId, List<String> receivers, String content, String orgId) {

        OrganizationDTO organizationDTO = ticketBaseUserGrpcService.getOrganization(orgId);
        if (Objects.isNull(organizationDTO)) {
            log.error("[Ticket] ticket notification sms get org failed; OrgId {}", orgId);
            return;
        }

        int smsType = SMSRecordTypeEnum.NOTIFICATION.getIndex();
        // 转换receivers为手机号码列表
        if (Objects.nonNull(receivers)) {
            Map<String, List<String>> mobileMap = new HashMap<>();
            for (String receiver : receivers) {
                if (!ObjectUtils.isEmpty(receiver)) {
                    List<String> mobileList = new ArrayList<>();
                    mobileList.add(receiver);
                    mobileMap.put(receiver, mobileList);
                }
            }

            // 遍历手机号码列表
            if (CollectionUtils.isEmpty(mobileMap)) {
                log.error("[Ticket] sendNotificationSms mobileMap is null");
                return;
            }
            for (String phone : mobileMap.keySet()) {
                List<String> phones = mobileMap.get(phone);
                if (CollectionUtils.isEmpty(phones)) {
                    log.error("[Ticket] sendNotificationSms phones is null");
                    return;
                }
                if (!CollectionUtils.isEmpty(phones)) {
                    // 调短信接口
                    if (log.isInfoEnabled()) {
                        log.info("[Ticket] sendNotificationSms 发送短信 surveyId {}", surveyId);
                    }
                    notifyUtil.sendSms(organizationDTO.getId(), surveyId, "", content, phone, smsType);
                }
            }
        }
    }


    /**
     * 计算周期行任务通知
     *
     * @author gy.liu
     * @date 2021/8/30
     */
    public void calPeriodNotification() {
        LocalDateTime now = LocalDateTime.now();
        int weekDay = now.getDayOfWeek().getValue(), day = now.getDayOfMonth();
        List<Notification> notificationFreqList = new ArrayList<>();
        List<Notification> notificationRatioList = new ArrayList<>();

        // 查询问卷的通知信息
        List<Notification> notifications = notificationDao.findBySurveyNotNull();
        if (CollectionUtils.isEmpty(notifications)) {
            log.error("[Ticket] calPeriodNotification notifications is null");
            return;
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calPeriodNotification notifications {}", notifications.size());
        }
        if (!CollectionUtils.isEmpty(notifications)) {
            for (Notification notification : notifications) {
                if (Objects.isNull(notification)) {
                    continue;
                }
                if (ObjectUtils.isEmpty(notification.getSurveyId())) {
                    continue;
                }

                // 查询问卷信息
                if (log.isDebugEnabled()) {
                    log.debug("[Ticket] calPeriodNotification notification:{}", JSONObject.toJSONString(notification));
                }
                QdesDTO project = ticketBaseQdesGrpcService.getQdesProject(notification.getSurveyId());
                if (log.isDebugEnabled()) {
                    log.debug("[Ticket] calPeriodNotification getSurveyProject:{}", JSONObject.toJSONString(project));
                }
                if (Objects.isNull(project)) {
                    log.error("[Ticket] calPeriodNotification 查询问卷失败 {}", notification.getId());
                    continue;
                }

                if (!ProjectStatusEnum.ACTIVATED.getIndex().equals(project.getStatus())) {
                    // 问卷不是激活状态不需要判断是否通知
                    continue;
                }

                if (Objects.isNull(notification.getRule())) {
                    continue;
                }

                List<Notification> freqList = new ArrayList<>();
                if (Objects.isNull(notification.getRule().getFreqEnable())) {
                    notification.getRule().setFreqEnable(false);
                }
                // 回收频次提醒
                if (notification.getRule().getFreqEnable()) {
                    String freqTime = notification.getRule().getFreqTime();
                    if (NOTIFICATION_TYPE_DAY.equals(freqTime)) {
                        // 如果是按天提醒
                        freqList.add(notification);
                    } else if (NOTIFICATION_TYPE_WEEK.equals(freqTime) && weekDay == 1) {
                        // 如果是按周提醒 & 周一
                        freqList.add(notification);
                    } else if (NOTIFICATION_TYPE_MONTH.equals(freqTime) && day == 1) {
                        // 如果是按月提醒 & 1号
                        freqList.add(notification);
                    }
                    if (log.isInfoEnabled()) {
                        log.info("[Ticket] calPeriodNotification freqList:{}", JSONObject.toJSONString(freqList));
                    }
                }

                List<Notification> ratioList = new ArrayList<>();
                if (Objects.isNull(notification.getRule().getRatioEnable())) {
                    notification.getRule().setRatioEnable(false);
                }
                // 回收占比提醒答卷数
                if (notification.getRule().getRatioEnable()) {
                    String ratioTime = notification.getRule().getRatioTime();
                    if (NOTIFICATION_TYPE_DAY.equals(ratioTime)) {
                        // 如果是按天提醒calPeriodNotification calcRspdFreq start:
                        ratioList.add(notification);
                    } else if (NOTIFICATION_TYPE_WEEK.equals(ratioTime) && weekDay == 1) {
                        // 如果是按周提醒 & 周一
                        ratioList.add(notification);
                    } else if (NOTIFICATION_TYPE_MONTH.equals(ratioTime) && day == 1) {
                        // 如果是按月提醒 & 1号
                        ratioList.add(notification);
                    }
                    if (log.isInfoEnabled()) {
                        log.info("[Ticket] calPeriodNotification ratioList:{}", JSONObject.toJSONString(ratioList));
                    }
                }


                for (Notification freq : freqList) {
                    // 计算统计回收频次通知提醒
                    try {
                        Notification res = calcRspdFreq(freq);
                        if (Objects.nonNull(res)) {
                            notificationFreqList.add(res);
                        }
                    } catch (Exception e) {
                        log.error("[Ticket] 回收频次提醒报错 projectId {}", freq.getSurveyId(), e);
                    }
                }
                for (Notification ratio : ratioList) {
                    // 计算统计回收环比通知提醒
                    try {
                        Notification res = calcRspdRatio(ratio);
                        if (Objects.nonNull(res)) {
                            notificationRatioList.add(res);
                        }
                    } catch (Exception e) {
                        log.error("[Ticket] 回收占比提醒报错 projectId{}", ratio.getSurveyId(), e);
                    }
                }
            }

            // 发送消息到延迟队列, 等待上午 09:00 发出
            // 实际发送消息方法见 {@see com.bestcem.xm.ticket.task.NotificationTask.receiveNotifyMq}

            // 回收频次提醒

            if (CollectionUtils.isEmpty(notificationFreqList)) {
                log.error("[Ticket] calPeriodNotification freq list is null");
            } else {
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] calPeriodNotification freq 发送mq{}", notificationFreqList.size());
                }
                com.bestcem.xm.ticket.service.dto.mq.TicketSendDTO ticketSendDTOFreq = new TicketSendDTO();
                ticketSendDTOFreq.setType(NotificationTypeEnum.FREQ_RSPD.getIndex());
                ticketSendDTOFreq.setData(notificationFreqList);
//                ticketDelayMqSender.sendNotifyDelayMq(ticketSendDTOFreq, getSendMqDelayDate(), "回收频次提醒");
                ticketMessageService.ticketSendNotifySend(ticketSendDTOFreq, getSendMqDelayDate(), "回收频次提醒");
            }
            // 回收环比提醒
            if (CollectionUtils.isEmpty(notificationRatioList)) {
                log.error("[Ticket] calPeriodNotification ratio list is null");
            } else {
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] calPeriodNotification ratio 发送mq{}", notificationRatioList.size());
                }
                TicketSendDTO ticketSendDTORatio = new TicketSendDTO();
                ticketSendDTORatio.setType(NotificationTypeEnum.RATIO_RSPD.getIndex());
                ticketSendDTORatio.setData(notificationRatioList);
//                ticketDelayMqSender.sendNotifyDelayMq(ticketSendDTORatio, getSendMqDelayDate(), "回收环比提醒");
                ticketMessageService.ticketSendNotifySend(ticketSendDTORatio, getSendMqDelayDate(), "回收环比提醒");
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] calPeriodNotification ratio 发送mq结束");
                }
            }
        }
    }

    /**
     * 发送 计算周期行任务通知 提醒消息
     *
     * @author gy.liu
     * @date 2021/9/9
     */
    public void receiveNotifyMq(com.bestcem.xm.ticket.service.dto.mq.TicketSendDTO ticketSendDTO) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] notifyHelper begin ticketSendDTO {}", ticketSendDTO.getData());
        }
        try {
            if (Objects.nonNull(ticketSendDTO)) {
                List<Notification> notifications = ticketSendDTO.getData();
                if (!CollectionUtils.isEmpty(notifications)) {
                    for (Notification notification : notifications) {
                        notificationUtil.notifyHelper(notification, "", ticketSendDTO.getType(), notification.getSurveyId(), null);
                    }
                }
            }
        } catch (Exception e) {
            log.error("[Ticket] receiveNotifyMq error", e);
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket] notifyHelper begin ticketSendDTO {}", ticketSendDTO.getType());
        }
    }


    // ========== private ==========


    /**
     * 获取 计算周期行任务通知 通知信息的发送时间
     *
     * @return 实际发送时间
     */
    private Date getSendMqDelayDate() {
        String[] split = notifyTime.split("-");
        // 默认为上午 9点发短信
        if (split.length < 3) {
            split = new String[]{"9", "0", "0"};
        }
        int hour, minute, second;
        try {
            hour = Integer.parseInt(split[0]);
            minute = Integer.parseInt(split[1]);
            second = Integer.parseInt(split[2]);
        } catch (Exception e) {
            // 默认为上午 9点发短信
            hour = 9;
            minute = 0;
            second = 0;
        }

        LocalDate localDate = LocalDate.now();
        LocalTime localTime = LocalTime.of(hour, minute, second, 0);
        LocalDateTime sendTime = LocalDateTime.of(localDate, localTime);
        return Date.from(sendTime.toInstant(BEIJING_ZONE));
    }

    /**
     * 计算统计回收频次通知提醒
     *
     * @param notification
     * @author gy.liu
     * @date 2021/8/26
     */
    public Notification calcRspdFreq(Notification notification) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calPeriodNotification calcRspdFreq start:{}", JSONObject.toJSONString(notification));
        }
        String time = notification.getRule().getFreqTime();
        String compare = notification.getRule().getFreqOperator();
        Integer value = notification.getRule().getFreqValue();

        if (ObjectUtils.isEmpty(time) || ObjectUtils.isEmpty(compare) || Objects.isNull(value) || Integer.valueOf(0).equals(value)) {
            log.error("[Ticket] calcRspdFreq notification id {}", notification.getId());
            return null;
        }

        // 时间转换
        EmailContentDTO emailContentDTO = parseTimeOperator(time);
        if (Objects.isNull(emailContentDTO)) {
            log.error("[Ticket] calcRspdFreq notification time {}", time);
            return null;
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calPeriodNotification parseTimeOperator:{}", JSONObject.toJSONString(emailContentDTO));
        }

        // 根据survey_id, start_time, end_time获取rspd数量
        LocalDateTime startTime = emailContentDTO.getLastStartTime(), endTime = emailContentDTO.getThisStartTime();
        String surveyId = notification.getSurveyId();
        Integer count = ticketSurveyGrpcService.getFinishedRespondentCountByTimeRange(surveyId, startTime, endTime);
        if (Objects.isNull(count)) {
            log.error("[Ticket] calcRspdFreq notification startTime {}", startTime);
            return null;
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calPeriodNotification calcRspdFreq:{}", count);
        }

        if (compareOperator(compare, BigDecimal.valueOf(count), BigDecimal.valueOf(value))) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] calPeriodNotification calcRspdFreq end:{}", JSONObject.toJSONString(notification));
            }
            //notifyHelper(notification, "", NotificationTypeEnum.FREQ_RSPD.getIndex(), notification.getSurveyId());
            return notification;
        }
        return null;
    }

    /**
     * 计算统计回收环比通知提醒
     *
     * @param notification
     * @author gy.liu
     * @date 2021/8/26
     */
    public Notification calcRspdRatio(Notification notification) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calPeriodNotification calcRspdRatio start:{}", JSONObject.toJSONString(notification));
        }
        String time = notification.getRule().getRatioTime();
        String compare = notification.getRule().getRatioOperator();
        Integer value = notification.getRule().getRatioValue();

        if (ObjectUtils.isEmpty(time) || ObjectUtils.isEmpty(compare) || Objects.isNull(value) || Integer.valueOf(0).equals(value)) {
            log.error("[Ticket] calcRspdRatio notification id {}", notification.getId());
            return null;
        }

        String surveyId = notification.getSurveyId();

        EmailContentDTO yesterday = parseTimeOperator(time);
        EmailContentDTO preYesterday = parseBTimeOperator(time);
        if (Objects.isNull(yesterday) || Objects.isNull(preYesterday)) {
            return null;
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calPeriodNotification calcRspdRatio:{},preYesterday:{}", JSONObject.toJSONString(yesterday), JSONObject.toJSONString(preYesterday));
        }
        // 根据survey_id, start_time, end_time获取rspd数量
        LocalDateTime startTime = yesterday.getLastStartTime(), endTime = yesterday.getThisStartTime();
        // 昨天、上周、上个月完成数
        Integer count = ticketSurveyGrpcService.getFinishedRespondentCountByTimeRange(surveyId, startTime, endTime);
        if (Objects.isNull(count)) {
            log.info("[Ticket] calcRspdRatio yesterday is null:{}", count);
            return null;
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calcRspdRatio yesterday:{}", count);
        }
        // 根据survey_id, start_time, end_time获取rspd数量
        LocalDateTime bStartTime = preYesterday.getLastStartTime(), bEndTime = preYesterday.getThisStartTime();
        // 前天、上上周、上上个月完成数
        Integer bCount = ticketSurveyGrpcService.getFinishedRespondentCountByTimeRange(surveyId, bStartTime, bEndTime);
        if (Objects.isNull(bCount) || Integer.valueOf(0).equals(bCount)) {
            log.info("[Ticket] calcRspdRatio pre yesterday is null:{}", bCount);
            return null;
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calcRspdRatio pre yesterday:{}", bCount);
        }
        //  res = (count - bCount) / bCount * 100
        BigDecimal res = (BigDecimal.valueOf(count).subtract(BigDecimal.valueOf(bCount)))
                .divide(BigDecimal.valueOf(bCount), 10, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));
        if (log.isInfoEnabled()) {
            log.info("[Ticket] calcRspdRatio res:{}", res);
        }
        if (compareOperator(compare, res, BigDecimal.valueOf(value))) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] calPeriodNotification calcRspdRatio end:{}", JSONObject.toJSONString(notification));
            }
            //notifyHelper(notification, "", NotificationTypeEnum.RATIO_RSPD.getIndex(), notification.getSurveyId());
            return notification;
        }

        return null;
    }

    /**
     * 昨天、上周、上个月
     *
     * @param operator
     * @author gy.liu
     * @date 2021/8/26
     */
    private static EmailContentDTO parseTimeOperator(String operator) {
        EmailContentDTO emailContentDTO = new EmailContentDTO();
        LocalDate localDate = LocalDate.now();
        LocalTime localTime = LocalTime.of(0, 0, 0, 0);
        LocalDateTime now = LocalDateTime.of(localDate, localTime);

        if (operator.equals(NotificationTask.NOTIFICATION_TYPE_DAY)) {
            LocalDateTime yesterday = now.minusDays(1);
            emailContentDTO.setLastStartTime(yesterday);
            emailContentDTO.setThisStartTime(now);
            return emailContentDTO;
        } else if (operator.equals(NotificationTask.NOTIFICATION_TYPE_WEEK)) {
            LocalDateTime lastStart = now.minusDays(now.getDayOfWeek().getValue() + 6);
            LocalDateTime lastEnd = now.minusDays(now.getDayOfWeek().getValue() - 1);
            emailContentDTO.setLastStartTime(lastStart);
            emailContentDTO.setThisStartTime(lastEnd);
            return emailContentDTO;
        } else if (operator.equals(NotificationTask.NOTIFICATION_TYPE_MONTH)) {
            LocalDateTime thisMonthStart = LocalDateTime.of(now.getYear(), now.getMonth(), 1, 0, 0);
            LocalDateTime lastMonthEnd = thisMonthStart.minusDays(1);
            LocalDateTime lastMonthStart = LocalDateTime.of(lastMonthEnd.getYear(), lastMonthEnd.getMonth(), 1, 0, 0);
            emailContentDTO.setLastStartTime(lastMonthStart);
            emailContentDTO.setThisStartTime(thisMonthStart);
            return emailContentDTO;
        }
        log.error("[Ticket] 通知提醒时间操作符不存在{}", operator);
        return null;
    }

    /**
     * 前天、上上周、上上个月
     *
     * @param operator
     * @author gy.liu
     * @date 2021/8/26
     */
    private static EmailContentDTO parseBTimeOperator(String operator) {
        EmailContentDTO emailContentDTO = new EmailContentDTO();
        LocalDate localDate = LocalDate.now();
        LocalTime localTime = LocalTime.of(0, 0, 0, 0);
        LocalDateTime now = LocalDateTime.of(localDate, localTime);

        if (operator.equals(NotificationTask.NOTIFICATION_TYPE_DAY)) {
            LocalDateTime yesterday = now.minusDays(2);
            emailContentDTO.setLastStartTime(yesterday);
            emailContentDTO.setThisStartTime(now.minusDays(1));
            return emailContentDTO;
        } else if (operator.equals(NotificationTask.NOTIFICATION_TYPE_WEEK)) {
            LocalDateTime lastStart = now.minusDays(now.getDayOfWeek().getValue() + 13);
            LocalDateTime lastEnd = now.minusDays(now.getDayOfWeek().getValue() + 7);
            emailContentDTO.setLastStartTime(lastStart);
            emailContentDTO.setThisStartTime(lastEnd.plusDays(1));
            return emailContentDTO;
        } else if (operator.equals(NotificationTask.NOTIFICATION_TYPE_MONTH)) {
            LocalDateTime thisMonthStart = LocalDateTime.of(now.getYear(), now.getMonth(), 1, 0, 0);
            LocalDateTime lastMonthEnd = thisMonthStart.minusDays(1);
            thisMonthStart = LocalDateTime.of(lastMonthEnd.getYear(), lastMonthEnd.getMonth(), 1, 0, 0);
            lastMonthEnd = thisMonthStart.minusDays(1);
            LocalDateTime lastMonthStart = LocalDateTime.of(lastMonthEnd.getYear(), lastMonthEnd.getMonth(), 1, 0, 0);
            emailContentDTO.setLastStartTime(lastMonthStart);
            emailContentDTO.setThisStartTime(thisMonthStart);
            return emailContentDTO;
        }
        log.error("[Ticket] 通知提醒时间操作符不存在{}", operator);
        return null;
    }

    /**
     * 根据操作符号返回比较结果
     *
     * @param operator
     * @param count
     * @param value
     * @author gy.liu
     * @date 2021/8/26
     */
    private Boolean compareOperator(String operator, BigDecimal count, BigDecimal value) {
        if ("lt".equals(operator)) {
            return count.compareTo(value) == -1;
        } else if ("le".equals(operator)) {
            return count.compareTo(value) == -1 || count.compareTo(value) == 0;
        } else if ("eq".equals(operator)) {
            return count.compareTo(value) == 0;
        } else if ("ne".equals(operator)) {
            return count.compareTo(value) == -1 || count.compareTo(value) == 1;
        } else if ("ge".equals(operator)) {
            return count.compareTo(value) == 1 || count.compareTo(value) == 0;
        } else if ("gt".equals(operator)) {
            return count.compareTo(value) == 1;
        }
        return false;
    }
}
